/*
* tedi2tex library code
* Copyright (C) <2022>  <alkeon> [alkeon@autistici.org]
   
* Texdi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Texdi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with tedi2tex.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

#include "tedi2tex.h"
#include "../tedi2lang/tedi2lang.h"
#include "../tedi2lang/exception.h"

using namespace std;

#define BLOCK 2
#define IMAGE 1
#define LINK 0
#define NO_TAG -1

tedi2tex::tedi2tex(tags_definition td): tedi2lang(td){}

tedi2tex::tedi2tex(): tedi2lang(default_tags_value()){}

tags_definition tedi2tex::default_tags_value(){
	
	tags_definition td;

	td.start_heading_first_level_tag = "\\section{";
	td.start_heading_second_level_tag = "\\subsection{";
	td.start_heading_third_level_tag = "\\subsubsection{";
	td.start_heading_fourth_level_tag = "\\subsubsection{";

	td.end_heading_first_level_tag = "}";
	td.end_heading_second_level_tag = "}";
	td.end_heading_third_level_tag = "}";
	td.end_heading_fourth_level_tag = "}";

	td.start_list_tag = "\\begin{itemize}";
	td.list_item_tag = "\\item ";
	td.end_list_tag = "\\end{itemize}";

	td.start_container_tag = "";
	td.middle_container_tag = "";
	td.end_container_tag = "";

	td.start_link_tag = "\\href{";
	td.middle_link_tag = "}{";
	td.end_link_tag = "}";

	td.start_image_tag = "\\begin{figure}[h!]\n\\centering\n\\includegraphics[width=1\\textwidth]{";
	td.middle_image_tag = "";
	td.end_image_tag = "";

	td.start_table_tag = "";
	td.end_table_tag = "\\hline\n\\end{longtable}\n";
	td.start_table_row_tag = "\\hline";
	td.end_table_row_tag = "\\tabularnewline";
	td.start_table_data_tag = " & ";
	td.end_table_data_tag = "";

	td.end_paragraph_tag = "\n";

	return td;
}

/*
 * Main logic converting line. 
 *
 */
string tedi2tex::convert_line(string& line) {

	if(line[0] != '<') {

		escape_underscores(line);

		string end_table = convert_end_table(line);
		size_t hash = line.find('#');
		if(found(hash) && is_first_tag(line, hash))
			return end_table + convert_line_heading(line, hash);
		else {
			size_t ul = line.find("__");
			if(found(ul))
				return end_table + convert_line_list_start(line);
			else {

				size_t li = line.find("--");
				if(found(li) && _is_unordered_list)
					convert_line_list_item(line, li);
				
				size_t ul_end = line.find(",,");
				if(found(ul_end) && _is_unordered_list)
					return end_table + convert_line_list_end(line);
				else{
					if(line[0] == '"')
						convert_line_quote(line);
					else
						convert_unquoted_tags(line);
					
					size_t first_pipe = line.find("|");
					if(found(first_pipe))
						return convert_line_table(line, first_pipe);
					else
						return end_table + convert_line_ending(line);
					
				}
			}
		}
	} else
		return convert_end_table_control_tags(line) + convert_line_control_tags(line);
	
}

/*
 * Deletes heading tag and insert image tag
 * - ([caption] image.png)
 * - image::image.png[caption]
 *
 */
void tedi2tex::convert_line_image(string& line) {

	size_t start_tag = get_not_escaped_tag(line, "([");
	if(found(start_tag) && !is_tag_escaped(line, start_tag)) {

		line = line.erase(start_tag, 2);

		size_t square_bracket = get_not_escaped_tag(line, "] ");
		if(found(square_bracket) && !is_tag_escaped(line, square_bracket)) {

			string caption = line.substr(start_tag, square_bracket - start_tag);

			line = line.erase(start_tag, square_bracket + 2 - start_tag);
			line = line.insert(start_tag, _start_image_tag);

			int last_parenthesis = correct_position(line, start_tag + _start_image_tag.size(), '(', ')');
			if(found(last_parenthesis)) {
				line = line.erase(last_parenthesis, 1);
				line = line.insert(last_parenthesis, "}\n\\caption{" + caption + "}\n\\end{figure}");
			} else
				throw Invalid("Missing ')' in images tag.",line);
		} else
			throw Invalid("Missing ']' in images tag.",line);
	} else
		throw Invalid("Missing images tag.",line);

}

/*
 * Deletes block tag and insert block tag
 *
 */
void tedi2tex::convert_line_block(string& line) {

	size_t start_tag = get_not_escaped_tag(line, "{(");
	if(found(start_tag) && !is_tag_escaped(line, start_tag)) {

		line = line.erase(start_tag, 2);
		line = line.insert(start_tag, _start_container_tag);

		size_t parenthesis = get_not_escaped_tag(line, ") ");
		if(found(parenthesis) && !is_tag_escaped(line, parenthesis)) {
			line = line.erase(start_tag, parenthesis - start_tag + 2);

			size_t end_tag = correct_position(line, start_tag, '{', '}');
			if(found(end_tag))
				line = line.erase(end_tag, 1);
			else
				++_open_brackets;
			
		} else
			throw Invalid("Missing ')' in block tag.",line);
	} else
		throw Invalid("Missing block tag.",line);

	_has_block = true;
}

/*
 * Start or continue table conversion
 *
 */
string tedi2tex::convert_line_table(string& line, size_t first_pipe) {
	string return_text;
	if(!_is_converting_table) {
		if(first_pipe != line.rfind("|")) {
			return_text += get_table_start_tag(line);
			_is_converting_table = true;
			return return_text + convert_line_table_row(line);
		} else
			return convert_line_ending(line);
	}else
		return return_text + convert_line_table_row(line);
	
	return return_text;
}

/*
 * Convert every cell of one table row
 *
 */
string tedi2tex::convert_line_table_row(string& line) {

	size_t pipe = line.find("|");
	if(found(pipe)) {
		++pipe;
		string return_text = _start_table_row_tag + "\n";
		line = line.substr(pipe, line.size() - 1);
		pipe = line.find("|");
		if(found(pipe)) {
			int size = line.size() - 1;
			if(line[size] == '|') {
				bool start = true;
				while(found(pipe) && line[size] == '|') {
					if(start) {
						return_text += strip_escaping(line.substr(0, pipe));
						start = false;
					} else
						return_text += _start_table_data_tag + strip_escaping(line.substr(0, pipe));
					++pipe;
					line = line.substr(pipe, size);
					pipe = line.find("|");
					size = line.size() - 1;
				}
				return return_text + _end_table_row_tag + "\n";
			}else
				throw Invalid("Table not correctly written\n"
					"Maybe there's a whitespace at end of line", line);
		}else
			throw Invalid("Expected '|' in table", line);
	}else
		throw Invalid("Expected '|' in table", line);
}

string tedi2tex::convert_line_control_tags(string& line){
	if(line[1] != '!') {
		if(line[1] == '>') {
			line = line.erase(0,2);
			return line + "\n";
		} else if(line[1] == '+') {
			line = line.erase(0,2);
			return "\\begin{verbatim}\n" + line + "\n\\end{verbatim}\n\n";

		} else if(!found(line.find("!")) && !found(line.find("¡"))) {
			line = line.erase(0,1);
			return "\\verb!" + line + "!\n";
		} else if(!found(line.find("}")) && !found(line.find("{"))) {
			line = line.erase(0,1);
			return "\\verb{" + line + "}\n";
		} else {
			line = line.erase(0, 1);
			return "\\begin{verbatim}\n" + line + "\n\\end{verbatim}\n\n";
		}
	} else
		return "";
	
}

string tedi2tex::get_table_start_tag(string& line) {

	int columns = get_table_columns(line);
	string start_table_tag = "\\begin{longtable}{";
	double size_per_column = 0.9 / columns;
	for(int i = 0; i < columns; ++i)
		start_table_tag += "|p{" + to_string(size_per_column) + "\\linewidth}";
	start_table_tag += "|}\n";
	return start_table_tag;
}

int tedi2tex::get_table_columns(string line){
	int data = 0;
	size_t pipe = line.find("|");
	int size = line.size() - 1;
	while(found(pipe) && (line[size] == '|' || line[size - 1] == '|')) {
		++pipe;
		line = line.substr(pipe, line.size() - 1);
		pipe = line.find("|");
		size = line.size() - 1;
		++data;
	}
	return data - 1;
}

void tedi2tex::escape_underscores(string& line) {
	size_t underscore = line.find('_');
	while(found(underscore)) {
		if(underscore + 1 >= line.size() || line[underscore + 1] != '_')
			line = line.insert(underscore, 1, '\\');
		underscore = line.find('_', underscore + 2);
	}
}
